#!/usr/bin/env bash
# Copyright (c) 2024 maminjie <canpool@163.com>
# SPDX-License-Identifier: MulanPSL-2.0

ROOT_DIR=$(dirname $(readlink -f "$0"))
CUR_DIR=$(pwd)
WORK_DIR=$CUR_DIR/rpm_backup_workspace

BACKUP_DIR=$WORK_DIR/backup
NOREPLACE_DIR=$WORK_DIR/noreplace

BACKUP_CONF_FLAG=0
NOREPLACE_CONF_FLAG=0


usage() {
printf "backup rpm files

usage:
    bash rpm_backup.sh [-h] [-c] [-n]

optional arguments:
    -c             back up the modified configuration files
    -n             back up the noreplace configuration files
    -h             show the help message and exit
\n"
}


log_error() {
    echo "ERROR: $1"
}

log_info() {
    echo "INFO: $1"
}


parse_cmdline() {
    if [ $# -eq 0 ]; then
        usage; exit 0
    fi

    local has_flag=0
    # parse cmd line arguments
    while getopts ":cnh" opt; do
        case "$opt" in
            c)
                BACKUP_CONF_FLAG=1
                has_flag=1
            ;;
            n)
                NOREPLACE_CONF_FLAG=1
                has_flag=1
            ;;
            h)
                usage; exit 0
            ;;
            ?)
                echo "please check the params."; usage; return 1
            ;;
        esac
    done
    if [ $has_flag -eq 0 ]; then
        return 1
    fi

    return 0
}

check_user() {
    if [[ $EUID -ne 0 ]]; then
        log_error "This script must be run as root!"
        exit 1
    fi
}

check_requires() {
    local ret=0

    for c in rpm; do
        command -v $c > /dev/null
        if [ $? -ne 0 ]; then
            log_error "command $c not found"
            ret=1
        fi
    done

    return $ret
}

init_dir() {
    [[ ! -d "$1" ]] && mkdir -p "$1"
}

clean_dir() {
    [[ -d "$1" ]] && rm -rf "$1"
}

backup_config_files() {
    local backup_result_file="$WORK_DIR/backup_result.txt"

    if [ $BACKUP_CONF_FLAG -eq 0 ]; then
        return
    else
        clean_dir $BACKUP_DIR
        rm -f $backup_result_file
    fi

    init_dir $BACKUP_DIR

    local rpm_opts="-V"

    if [ $BACKUP_CONF_FLAG -eq 1 ]; then
        rpm_opts="$rpm_opts -c"
    fi

    local pkgs="$(rpm -qa | sort -u)"
    for pkg in $pkgs; do
        local files=$(rpm $rpm_opts $pkg | grep "^..5" | awk '{print $NF}')
        for file in $files; do
            echo "$pkg,$file" | tee -a $backup_result_file
            cp -r -a --parents $file $BACKUP_DIR
        done
    done
}

backup_noreplace_config_files() {
    local noreplace_result_file="$WORK_DIR/noreplace_result.txt"
    local noreplace_result_diff_file="$WORK_DIR/noreplace_result_diff.txt"

    if [ $NOREPLACE_CONF_FLAG -eq 0 ]; then
        return
    else
        clean_dir $NOREPLACE_DIR
        rm -f $noreplace_result_file $noreplace_result_diff_file
    fi

    init_dir $NOREPLACE_DIR

    local nr_files=$(find /etc -name "*.rpmnew" -o -name "*.rpmsave")
    i=1
    for nr_f in $nr_files; do
        cp -r -a --parents $nr_f $NOREPLACE_DIR
        local pkg=""
        local orig_f=${nr_f%.*}
        if [ -f "$orig_f" ]; then
            cp -r -a --parents $orig_f $NOREPLACE_DIR
            pkg=$(rpm -qf "$orig_f")
        else
            orig_f=""
        fi
        local a_f=""
        local b_f=""
        if [[ "$nr_f" =~ \.rpmnew$ ]]; then
            a_f=$orig_f
            b_f=$nr_f
        elif [[ "$nr_f" =~ \.rpmsave$ ]]; then
            a_f=$nr_f
            b_f=$orig_f
        fi
        echo "$pkg,$a_f,$b_f" | tee -a $noreplace_result_file
        echo "$i) $pkg,$a_f,$b_f" >> $noreplace_result_diff_file
        if [ -n "$orig_f" ]; then
            diff -u $a_f $b_f >> $noreplace_result_diff_file
        fi
        ((i++))
    done
}

do_main() {
    # init dirs
    init_dir $WORK_DIR

    # backup files
    backup_config_files
    backup_noreplace_config_files

    # clean
    cd $CUR_DIR

    return 0
}

main() {
    check_user

    parse_cmdline "$@"
    if [ $? -ne 0 ]; then
        log_error "parse params failed, try -h for help"
        return 1
    fi

    check_requires
    if [ $? -ne 0 ]; then
        log_error "check requires failed"
        return 1
    fi

    do_main
    if [ $? -ne 0 ]; then
        return 1
    fi

    return 0
}

main "$@"
